package com.huijie.app.sys.aspect;

import com.huijie.core.bean.ResponseCodeMsgBean;
import com.huijie.core.util.ResponseMsgUtil;
import net.sf.json.JSONObject;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.NamedThreadLocal;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.Set;

import static com.huijie.core.util.ResponseMsgUtil.ERROR_CODE_SYSTEM;
import static com.huijie.core.util.ResponseMsgUtil.VALIDATOR_CODE_SYSTEM;


/**
 * @ClassName SystemCommonAspect
 * @Description Spring AOP实现restController管理，通用切点
 * @Author Alex
 * @Date 2018/10/12 14:08
 * @Version 1.0
 */
@Aspect
@Component
public class RestControllerAspect {

    private Logger logger = LogManager.getLogger(RestControllerAspect.class);

    private static final ThreadLocal<Date> THREAD_LOCAL_ERROR_BEGIN_TIME = new NamedThreadLocal<>("ThreadLocal error beginTime");

    @Autowired(required=false)
    private HttpServletRequest request;
    @Autowired()
    private PointcutSaveLog pointcutSaveLog;

    /**
     * restcontroller层切点
     */
    @Pointcut("execution(* com.huijie.*.controller.*.*Controller*.*(..))")
    public void restControllerAspect() {
        System.out.println("、\\\\\\\\\\\\\\\\\\\\\\\\\\\\");
    }

    /**
     * restAndValidControllerAspect层切点
     */
    //@javax.validation.Valid *,..
    // && args(bindingResult)
    //* com.cn.rfid.controller.bus.*Controller*.*()
    //, argNames = "bindingResult"
    //BindingResult bindingResult
    @Pointcut(value = "execution(* com.huijie.*.controller.*.*Controller*.*(..))")
    public void restAndValidControllerAspect() {

        System.out.println("bindingResult:::::::::::::::::::::::::::::::"/*+bindingResult*/);
    }
//    @Pointcut(value = "execution(* com.cn.rfid.controller.bus.*.*(..))")
//    public void restAndValidControllerAspect() {
//
//        System.out.println("bindingResult:::::::::::::::::::::::::::::::"/*+bindingResult*/);
//    }

    /**
     * 前置通知 (在方法执行之前返回)用于拦截Controller层记录用户的操作的开始时间
     * @param joinPoint 切点
     * @throws InterruptedException
     */
    @Before(value = "restControllerAspect()", argNames = "joinPoint")
    public void doBefore(JoinPoint joinPoint) throws InterruptedException{
        //线程绑定变量（该数据只有当前请求的线程可见）
//        String jwtTokenStr = request.getHeader("jwt_token");
//        jwtTokenThreadLocal.set(jwtTokenStr);
    }

//    /**
//     * 前置通知 (在方法执行之前返回)用于拦截Controller层记录用户的操作的开始时间
//     * @param joinPoint 切点
//     * @throws InterruptedException
//     */
//    //bindingResult
//    //, argNames = "pjp,bindingResult"
//    //,BindingResult bindingResult
//    @Before(value="restAndValidControllerAspect()")
//    public void doBeforeRestAndValid() throws InterruptedException{
//        ResponseCodeMsgBean r = new ResponseCodeMsgBean();
////        if(bindingResult.hasErrors()){
////            StringBuffer sb = ResponseMsgUtil.getValidatorErrors(bindingResult);
////            String message = null == sb ? "":sb.toString();
////            ResponseCodeMsgBean rcmb = new ResponseCodeMsgBean();
////            rcmb.setCode(VALIDATOR_CODE_SYSTEM);
////            rcmb.setMessage(message);
////            JSONObject j = JSONObject.fromObject(rcmb);
////            HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
////            response.setContentType("application/json");
////            response.setCharacterEncoding("utf-8");
////            ServletOutputStream servletOutputStream;
////            logger.error("========API Catch a InValid Params========", message);
////            try {
////                servletOutputStream = response.getOutputStream();
////                servletOutputStream.print(j.toString());
////                servletOutputStream.flush();
////                servletOutputStream.close();
////            } catch (IOException e1) {
////                logger.error("拦截webAPi校验时，outputStream报错");
////            }
////        }
//    }

//    @Around(value = "execution(* com.cn.rfid.controller.bus.*.*(..)) && args(.., bindingResult) "/* +
//            "&& @annotation(org.springframework.web.bind.annotation.PostMapping)"*/, argNames = "pjp,bindingResult")
//    private ResponseEntity<Object> doAround(ProceedingJoinPoint pjp, BindingResult bindingResult)  throws Exception {
//        ResponseCodeMsgBean r = new ResponseCodeMsgBean();
//        if(bindingResult.hasErrors()){
//            r = ResponseMsgUtil.returnCodeMessage(r, bindingResult);
//            return ResponseEntity.ok(r);
//        }
//        // result的值就是被拦截方法的返回值
//        Object result;
//        try {
//            result = pjp.proceed();
//            return (ResponseEntity<Object>) result;
//        } catch (Throwable throwable) {
//            throw new Exception(throwable);
//        }
//    }
//    @Before("@annotation(com.cn.rfid.architect.aspect.ParamValid)")
//    public void paramValid(JoinPoint point) {
//        Object[] paramObj = point.getArgs();
//        if (paramObj.length > 0) {
//            if (paramObj[1] instanceof BindingResult) {
//                BindingResult result = (BindingResult) paramObj[1];
//                StringBuffer stringBuffer = ResponseMsgUtil.getValidatorErrors(result);
//                String message = null == stringBuffer? "":stringBuffer.toString();
//                this.aspectResponse(message);
//            }
//        }
//    }

    @Around(value = "@annotation(com.huijie.app.sys.aspect.ControllerParamValid)",argNames = "pjp")
    public ResponseEntity<Object> paramValid(ProceedingJoinPoint pjp)  throws Exception {
        Object[] paramObj = pjp.getArgs();
        if (paramObj.length > 0) {
            if (paramObj[1] instanceof BindingResult) {
                BindingResult result = (BindingResult) paramObj[1];
                ResponseCodeMsgBean r = new ResponseCodeMsgBean();
                if(result.hasErrors()){
                    r = ResponseMsgUtil.returnCodeMessage(r, result);
                    return ResponseEntity.ok(r);
                }
            }
        }
        // result的值就是被拦截方法的返回值
        Object result;
        try {
            result = pjp.proceed();
            return (ResponseEntity<Object>) result;
        } catch (Throwable throwable) {
            throw new Exception(throwable);
        }
    }

    /**
     * 后置通知(在方法执行之后返回) 用于拦截Controller层操作
     * @param joinPoint 切点
     */
    @After("restControllerAspect()")
    public void after(JoinPoint joinPoint){

    }

    @Pointcut("execution(* com.huijie.*.controller.*.*Controller*.*(..))")
    private void webPointcut() {}

    /**
     * 前置通知 (在方法执行之前返回)用于拦截该切点记录用户的操作的开始时间
     * @param joinPoint 切点
     * @throws InterruptedException
     */
    @Before("webPointcut()")
    public void doWebPointcutBefore(JoinPoint joinPoint) throws InterruptedException{
        //线程绑定变量（该数据只有当前请求的线程可见）
        Date beginTime=new Date();
        THREAD_LOCAL_ERROR_BEGIN_TIME.set(beginTime);
    }

    /**
     * 拦截web层异常，记录异常日志，并返回友好信息到前端
     * 目前只拦截Exception，是否要拦截Error需再做考虑
     *
     * @param e 异常对象
     */
    @AfterThrowing(pointcut = "webPointcut()", throwing = "e")
    public void handleThrowing(JoinPoint point, Exception e) {
        ResponseCodeMsgBean rcmb = new ResponseCodeMsgBean();
        rcmb.setCode(ERROR_CODE_SYSTEM);
        rcmb.setMessage(e.getClass().getName() + ":" + e.getMessage());
        if(e instanceof ConstraintViolationException){
            //参数校验异常
            rcmb.setCode(VALIDATOR_CODE_SYSTEM);
            String msg = ",";
            Set<ConstraintViolation<?>> constraintViolations = ((ConstraintViolationException) e).getConstraintViolations();
            for (ConstraintViolation<?> item : constraintViolations) {
                //打印验证不通过的信息
                msg += item.getMessage();
            }
            rcmb.setMessage(msg.substring(1));
        }else {
            //其他异常打印
            logger.error("========API Catch a Unexpected Error========", e);
        }
        JSONObject j = JSONObject.fromObject(rcmb);
        HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
        response.setContentType("application/json");
        response.setCharacterEncoding("utf-8");
        ServletOutputStream servletOutputStream;
        try {
            String logType = "error";
            if(!(e instanceof ConstraintViolationException)){
                //其他异常打印
                pointcutSaveLog.controllerParamSaveLog(request, THREAD_LOCAL_ERROR_BEGIN_TIME,point,logType,e.toString());
            }
            servletOutputStream = response.getOutputStream();
            servletOutputStream.write(j.toString().getBytes("utf-8"));
            servletOutputStream.flush();
            servletOutputStream.close();
        } catch (IOException e1) {
            logger.error("拦截web异常时，outputStream报错");
        }finally {
            THREAD_LOCAL_ERROR_BEGIN_TIME.remove();
        }
    }

    public void aspectResponse(String message){
        ResponseCodeMsgBean rcmb = new ResponseCodeMsgBean();
        rcmb.setCode(VALIDATOR_CODE_SYSTEM);
        rcmb.setMessage(message);
        JSONObject j = JSONObject.fromObject(rcmb);
        HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
        response.setContentType("application/json");
        response.setCharacterEncoding("utf-8");
//        ServletOutputStream servletOutputStream;
        OutputStream output = null;
        logger.error("========API Catch a aspectResponse error========", message);
        try {
            output=response.getOutputStream();
            byte[]b=j.toString().getBytes(StandardCharsets.UTF_8);
            output.write(b);
        } catch (IOException e1) {
            logger.error("aspectResponse，outputStream报错");
        }finally {
            try {
                if (output != null) {
                    output.close();
                }
            } catch (IOException e) {
                logger.error(e.getMessage());
            }
        }
    }

}
